﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using LucasCode.PublicTypes;
using System.Globalization;
using System.Diagnostics;
using LucasCode.Tools;
using UserNotifier;
using ESLReminder.Games;
using System.ComponentModel;

namespace ESLReminder.Modules
{
    public class ReplayWatcher : ReminderModule
    {
        FileSystemWatcher m_ReplayWatcher;
        private long m_DemoTimerInterval = 5000;
        bool m_UseFileWatcherWorkaround = false;
        Timer DemoTimer = null;
        Timer FileSystemRefreshTimer = null;
        bool DemoWritten = false;
        private bool m_OverwriteProtectionEnabled = true;
        [DefaultValueAttribute(true)]
        public bool OverwriteProtectionEnabled
        {
            get
            {
                return m_OverwriteProtectionEnabled;
            }
            set
            {
                m_OverwriteProtectionEnabled = value;
            }
        }
        public long DemoTimerInterval
        {
            get { return m_DemoTimerInterval; }
            set
            {
                m_DemoTimerInterval = value;
                DemoTimer.Change(Timeout.Infinite, value);
            }
        }
        public ReplayWatcher()
            : base( new PluginInfo( "ReplayWatcher",
                                    new Version(1,0),
                                    "©2009 Lucas Romero",
                                    "http://code.google.com/p/quicktools/"),
                    "Watches for replays written to gamedir and warns if no write access is detected. "+
                    "Additionally renames every replay to include a timestamp which protects it from being accidentally overwritten.")
        {
            m_ReplayWatcher = new FileSystemWatcher();
            m_ReplayWatcher.Filter = "*.dem";
            m_ReplayWatcher.Changed += new FileSystemEventHandler(ReplayWatcher_Changed);
            m_ReplayWatcher.Created += new FileSystemEventHandler(ReplayWatcher_Changed);
            m_ReplayWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.CreationTime;
            m_ReplayWatcher.IncludeSubdirectories = true;
            DemoTimer = new Timer(new TimerCallback(DemoTimerTick));

            LogInfo("Operating System: " + Environment.OSVersion);

            //check for windows vista
            if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major == 6)
            {
                m_UseFileWatcherWorkaround = true;
            }
        }
        private void FileSystemRefresher(object dummy)
        {
            //Debug.WriteLine("Refreshing FileSystem");
            DirectoryInfo d = new DirectoryInfo(m_ReplayWatcher.Path);
            FileInfo[] files = d.GetFiles("*.dem", SearchOption.AllDirectories);
            foreach (FileInfo f in files)
            {
                f.Refresh();
            }
        }
        private void DemoTimerTick(object dummy)
        {
            if (!DemoWritten)
            {
                //Debug.WriteLine("No Demo recording!");
                m_Reminder.AddWarning(WarnEvents.NoReplay, Timeout.Infinite);
            }
            DemoWritten = false;
        }
        private MatchFile m_CurrentReplay = null;
        private string m_NewReplayFileName = "demo.dem";
        void ReplayWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            DemoWritten = true;
            m_Reminder.RemoveWarning(WarnEvents.NoReplay);
            if (!m_Reminder.CurrentArchive.containsFile(e.FullPath))
            {
                m_CurrentReplay = new MatchFile(e.FullPath);
                m_Reminder.CurrentArchive.AddFile(m_CurrentReplay);
                m_NewReplayFileName = Path.Combine(Path.GetDirectoryName(e.FullPath), Path.GetFileNameWithoutExtension(e.FullPath) + "_" + DateTime.Now.ToString("yy-MM-dd_HH-mm-ss", DateTimeFormatInfo.InvariantInfo) + ".dem");
            }
            if (m_OverwriteProtectionEnabled)
            {
                try
                {
                    File.Move(e.FullPath, m_NewReplayFileName);
                    m_CurrentReplay.Filename = m_NewReplayFileName;
                    LogInfo("Renamed " + e.FullPath + " to " + m_NewReplayFileName);
                }
                catch (IOException)
                {
                    /* file may be in use by hl/hl2 when still recording */
                }
            }
        }

        public override void PluginLoadedEx(IReminderInterface host)
        {
            host.GameStarted += new GameStartedEventHandler(reminder_GameStarted);
            host.GameEnded += new GameEndedEventHandler(reminder_GameEnded);
        }

        void reminder_GameEnded(IReminderInterface sender, GameEndedEventArgs args)
        {
            m_ReplayWatcher.EnableRaisingEvents = false;
            //Stop demo-timer
            DemoTimer.Change(Timeout.Infinite, Timeout.Infinite);
            if (FileSystemRefreshTimer != null)
                FileSystemRefreshTimer.Change(Timeout.Infinite, Timeout.Infinite);
        }

        void reminder_GameStarted(IReminderInterface sender, GameStartedEventArgs args)
        {
            Type t = args.Game.GetType();
            if (t != typeof(GameHalfLife) && t != typeof(GameHalfLife2))
                return;
            //TODO: catch error in accessing args.Game (potential access denied)
            string executabledir = Path.GetDirectoryName(args.Game.Process.MainModule.FileName);
            m_ReplayWatcher.Path = Path.Combine(executabledir, GameInteropModule.getModFromDir(executabledir));
            m_ReplayWatcher.EnableRaisingEvents = true;
            DemoTimer.Change(0, m_DemoTimerInterval);
            if (m_UseFileWatcherWorkaround)
            {
                LogInfo("Enabling workaround for Windows Vista FileSystemWatcher bug (may be slow when you got a lot of replay files laying around)");
                if (FileSystemRefreshTimer == null)
                    FileSystemRefreshTimer = new Timer(new TimerCallback(FileSystemRefresher));
                FileSystemRefreshTimer.Change(0, 3000);
            }
        }
    }
}
